package models;

import java.util.*;

import javax.persistence.*;

import org.apache.commons.mail.EmailException;

import play.data.validation.Email;
import play.data.validation.Required;

import play.db.jpa.*;

/**
 * User entity creates a user
 * 
 * @author Nourhan Mostafa Elsawa
 */
@Entity
public class User extends Model {

	@OneToMany(mappedBy = "user")
	public List<Permission> permission;

	/**
	 * The email of the user which should always be unique
	 * 
	 * @author Nourhan Mostafa Elsawa
	 */
	@play.data.validation.Email
	@Required
	public String email;

	/**
	 * The name of the user
	 * 
	 * @author Nourhan Mostafa Elsawa
	 */
	@Required
	public String name;
	/**
	 * Tha password of the user
	 * 
	 * @author Nourhan Mostafa Elsawa
	 */
	@Required
	public String password;
	/**
	 * The contribution counter is the counter that shows the existence of the
	 * user in certain communities
	 * 
	 * @author Nourhan Mostafa Elsawa
	 */
	@Required
	public int total_counter;
	@Required
	public boolean contribution_preference;
	@Required
	public boolean subscribe_preference;

	/**
	 * list of tags that the user is subscribed to
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToMany
	public List<Tag> tags;
	/**
	 * list of organizations that the user is blocked from
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToMany
	public List<Organization> blockedUsers;
	@ManyToMany
	public List<Organization> blocked;
	/**
	 * list of notifications that the user has
	 * 
	 * @author youstina galal
	 */

	@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
	public List<Notification> notifies;

	/**
	 * An Invitation is sent by only one user
	 * 
	 * @author Mai Selwanes
	 */
	@OneToMany(mappedBy = "receiver", cascade = CascadeType.ALL)
	public List<Invitation> invitations;

	/**
	 * Relation between the class user and entities is many to many as user can
	 * belong to one or more entity and an entity has one or more users
	 * 
	 * @author Mai Abu-El Seoud
	 */
	@ManyToMany
	@JoinTable(name = "usersentities")
	public List<Entities> userEntities;

	/**
	 * its the number of unread notifications that the user has
	 * 
	 * @author youstina galal
	 */
	public int unreadNotification;

	@OneToMany(mappedBy = "lead", cascade = CascadeType.ALL)
	public List<Organization> UsersOrg;

	@ManyToMany
	public List<Organization> orgRequested;

	public static List<Idea> ideasForOneUser(User Author) {
		return Idea.find("byAuthor", Author.name).fetch();
	}

	/**
	 * Each user has a list of drafts. Each user has one or more drafts and the
	 * draft belongs to only one user
	 * 
	 * @author Heba Khattab
	 * 
	 */
	@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
	public List<Draft> draftOfUser;

	/**
	 * Each user has one or more topics and the topic belongs to only one user
	 * 
	 * @author Malak El Kommos
	 * 
	 */

	@OneToMany(mappedBy = "author")
	public List<Topic> topic;

	/**
	 * The relation can be topic can have many subscribers and subscribers can
	 * subscribe on many topics
	 * 
	 * @author Nesreen elGohary
	 */
	@ManyToMany
	public List<Topic> topicSubscribed;
	/**
	 * Each user has one or more comments and the comment belongs to only one
	 * user.
	 * 
	 * @author Malak El Kommos
	 * 
	 */

	@OneToMany(mappedBy = "commentator")
	public List<Comment> comments;

	/**
	 * Each user has one or more ideas and the idea belongs to only one user.
	 * 
	 * @author Malak El Kommos
	 * 
	 */

	@OneToMany(mappedBy = "author")
	public List<Idea> idea;
	/**
	 * The Entity Organizer can be an organizer or an idea developer in one or
	 * more entites
	 * 
	 * @author Mai Abu-El Seoud
	 */
	@ManyToMany
	public List<Entities> entity;
	/**
	 * A user can belong to one or more organizations
	 * 
	 * @author Mai Abu-El Seoud
	 */
	@ManyToMany
	@JoinTable(name = "usersorganization")
	public List<Organization> organization;

	/**
	 * The list of logs made by the user
	 * 
	 * @author Mai Selwanes
	 * 
	 */
	@OneToMany(mappedBy = "performer", cascade = CascadeType.ALL)
	public List<Log> logs;

	/**
	 * The user's profile visibility
	 * 
	 * @author Malak El Kommos
	 * 
	 */

	public boolean viewed;

	@ManyToMany
	@JoinTable(name = "usersubscribedorg")
	public List<Organization> subscribedorgs;
	@ManyToMany
	@JoinTable(name = "usersubscribedentity")
	public List<Entities> subscriberentities;

	/**
	 * the relation can be created by one user and the user creates many
	 * relations
	 * 
	 * @author Menna Magdy
	 */
	@OneToMany(mappedBy = "user")
	public List<Relation> relationList;

	/**
	 * the user(organizer) can send many relation name request and the
	 * relationRequest only sent by one user
	 * 
	 * @author Menna Magdy
	 */
	@OneToMany(mappedBy = "sender", cascade = CascadeType.ALL)
	public List<RelationRequest> requestRelations;

	/**
	 * contributionCounter is the number counting the contribution of a user by
	 * posting topics, ideas or comment
	 * 
	 * @author Heba Khattab
	 */
	public int contributionCounter;

	/**
	 * each user can have zero or many communities
	 * 
	 * @author Nourhan El-Nehlawy
	 */
	@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL)
	public List<Community> ownedCommunityList;
	/**
	 * every user belongs to one or more communities
	 * 
	 * @author ismail omar
	 */
	@ManyToMany
	public List<Community> userCommunity;
	/**
	 * The list of request that the sender sent.
	 * 
	 * @author Shahir El Faramawy
	 */
	@OneToMany(mappedBy = "sender", cascade = CascadeType.ALL)
	public List<Request> senderRequest;
	/**
	 * The topics that the User Spammed
	 * 
	 * @author Shahir El Faramawy
	 */
	@JoinTable(name = "UserSpammedTopics")
	@ManyToMany
	public List<Topic> topicSpammed;
	/**
	 * Th ideas that the user spammed
	 * 
	 * @author Shahir El Faramawy
	 */
	@JoinTable(name = "UserSpammedIdeas")
	@ManyToMany
	public List<Idea> ideaSpammed;
	/**
	 * The list of comments that this user marked as spam
	 * 
	 * @author Shahir El Faramawy
	 */
	@JoinTable(name = "UserSpammedComments")
	@ManyToMany
	public List<Comment> commentSpammed;

	/**
	 * The constructor of the user
	 * 
	 * @param email
	 *            A String that indicates the mail of the user.
	 * @param name
	 *            A String that indicates the name of the user.
	 * @param password
	 *            A String that indicates the password of the user.
	 * @param totalCounter
	 *            An Integer that indicates the total counter of the user.
	 * @param viewed
	 *            A boolean that indicates the viewed status of the user.
	 * @param contributionPreference
	 *            A boolean that indicates the contribution preference status of
	 *            the user.
	 * @param subscribePreference
	 *            A boolean that indicates the subscribe preference status of
	 *            the user.
	 * 
	 * @author Nourhan Mostafa Elsawa
	 */

	public User(String email, String name, String password, int totalCounter,
			boolean viewed, boolean contributionPreference,
			boolean subscribePreference) {
		this.organization = new LinkedList<Organization>();
		draftOfUser = new ArrayList<Draft>();
		this.email = email;
		this.name = name;
		this.password = password;
		this.total_counter = totalCounter;
		this.contribution_preference = contributionPreference;
		this.subscribe_preference = subscribePreference;
		notifies = new ArrayList<Notification>();
		unreadNotification = 0;
		this.invitations = new ArrayList<Invitation>();
		this.topic = new LinkedList<Topic>();
		this.idea = new LinkedList<Idea>();
		this.comments = new LinkedList<Comment>();
		this.logs = new ArrayList<Log>();
		this.viewed = viewed;
		this.entity = new LinkedList<Entities>();
		this.UsersOrg = new ArrayList<Organization>();
		this.subscribedorgs = new ArrayList<Organization>();
		this.subscriberentities = new ArrayList<Entities>();

		this.requestRelations = new ArrayList<RelationRequest>();

		this.permission = new ArrayList<Permission>();
		this.relationList = new ArrayList<Relation>();
		this.contributionCounter = 0;
		this.userCommunity = new ArrayList<Community>();
		this.ownedCommunityList = new ArrayList<Community>();
		this.senderRequest = new ArrayList<Request>();
		this.topicSpammed = new ArrayList<Topic>();
		this.ideaSpammed = new ArrayList<Idea>();
		this.commentSpammed = new ArrayList<Comment>();
		this.blocked=new ArrayList<Organization>();
	}

	/**
	 * An empty constructor for the user
	 * 
	 * @author Nourhan Mostafa Elsawa
	 */
	public User() {

	}

	// /**
	// * checks if a certain user has a list of topics
	// *
	// * @author Malak El Kommos
	// *
	// * @return boolean boolean returns true if a user has topics
	// */
	//
	// public boolean hasTopic() {
	// if (this.topic.isEmpty())
	// return false;
	// else
	// return true;
	// }
	//
	// /**
	// * checks if a certain user has a list of ideas or
	// *
	// * @author Malak El Kommos
	// * @return boolean boolean returns true if a user has ideas
	// */
	//
	// public boolean hasIdea() {
	// if (this.idea.isEmpty())
	// return false;
	// else
	// return true;
	// }
	//
	// /**
	// * checks if a certain user has a list of comments
	// *
	// * @author Malak El Kommos
	// * @return boolean boolean returns true if a user has comments
	// */
	//
	// public boolean hasComment() {
	// if (this.comments.isEmpty())
	// return false;
	// else
	// return true;
	// }
	//
	// /**
	// * checks if a certain user has a list of entities
	// *
	// * @author Malak El Kommos
	// *
	// * @return boolean boolean returns true if a user has entities
	// */
	//
	// public boolean hasEntity() {
	// if (this.entity.isEmpty())
	// return false;
	// else
	// return true;
	// }
	//
	// /**
	// * checks if a certain user has a list of organizations
	// *
	// * @author Malak El Kommos
	// *
	// * @return boolean boolean returns true if a user has organizations
	// */
	//
	// public boolean hasOrganization() {
	// if (this.organization.isEmpty())
	// return false;
	// else
	// return true;
	// }

	// /**
	// * modifies the topics of a certain user by setting the author name to
	// * anonymous.
	// *
	// * added log statement inside the method to keep track of what happened
	// *
	// * @author Malak El Kommos
	// * @author Mai Selwanes
	// */
	//
	// public void modifyTopic() {
	//
	// User userName = this;
	// if (hasTopic() == true) {
	// List<Topic> topic = this.topic;
	// for (int i = 0; i < topic.size(); i++) {
	//
	// Log log = new Log(
	// topic.get(i).entity.organization,
	// this,
	// " named "
	// + userName.name
	// + " has deleted his profile and so his/her topic of title: "
	// + topic.get(i).title
	// + " has been set to have an anonymous author.");
	// log.save();
	// topic.get(i).author.name = "Anonymous";
	// this.save();
	// }
	//
	// }
	// }
	//
	// /**
	// * modifies the ideas of a certain user by setting the author name to
	// * anonymous.
	// *
	// * added log statement inside the method to keep track of what happened
	// *
	// * @author Malak El Kommos
	// * @author Mai Selwanes
	// */
	//
	// public void modifyIdea() {
	//
	// User userName = this;
	// if (hasIdea() == true) {
	// List<Idea> idea = this.idea;
	// for (int i = 0; i < idea.size(); i++) {
	// // Log log = new Log(
	// // idea.get(i).entity.organization,
	// // this,
	// // " named "
	// // + userName.name
	// // + " has deleted his profile and so his/her idea of title: "
	// // + idea.get(i).title
	// // + " has been set to have an anonymous author.");
	// // log.save();
	// idea.get(i).author.name = "Anonymous";
	// this.save();
	// }
	//
	// }
	// }
	//
	// /**
	// * modifies the Comments done by a certain user by setting the author name
	// * to anonymous.
	// *
	// *
	// * added log statement inside the method to keep track of what happened
	// *
	// * @author Malak El Kommos
	// * @author Mai Selwanes
	// */
	//
	// public void modifyComment() {
	// User userName = this;
	// if (hasComment() == true) {
	// List<Comment> comment = this.comments;
	// for (int i = 0; i < topic.size(); i++) {
	// // Log log = new Log(
	// // comment.get(i).idea.entity.organization,
	// // this,
	// // " named "
	// // + userName.name
	// // +
	// " has deleted his profile and so his/her comment has been set to have an anonymous author.");
	// // log.save();
	// comment.get(i).commentator.name = "Anonymous";
	// this.save();
	// }
	// }
	//
	// }
	//
	// /**
	// * sets the EntityUser and EntityOrganizaer's name to anonymous.
	// * and removes the subscriber to a certain entity from the list of
	// subscribers.
	// *
	// * added log statement inside the method to keep track of what happened
	// *
	// * @author Malak El Kommos
	// * @author Mai Selwanes
	// */
	//
	// public void modifyEntity() {
	//
	// User userName = this;
	// if (hasEntity() == true) {
	// List<Entities> entity = this.entity;
	// for (int i = 0; i < entity.size(); i++) {
	//
	// if (!entity.get(i).entityOrganizers.isEmpty())
	// for (int y = 0; y < entity.get(i).entityOrganizers.size(); y++) {
	// Log log = new Log(
	// entity.get(i).organization,
	// this,
	// " named "
	// + userName.name
	// + " has deleted his profile and so the/an organizer of entity: "
	// + entity.get(i).entityName
	// + " has been set to have an anonymous organizer.");
	// log.save();
	// entity.get(i).entityOrganizers.get(i).name = "Anonymous";
	// this.save();
	// }
	//
	// if (!entity.get(i).subscribers.isEmpty())
	// for (int z = 0; z < entity.get(i).subscribers.size(); z++) {
	// Log log = new Log(
	// entity.get(i).organization,
	// this,
	// " named "
	// + userName.name
	// +
	// " has deleted his profile and so has been removed from the list of subscribers of the entity : "
	// + entity.get(i).entityName + ".");
	// log.save();
	// entity.get(i).subscribers.remove(z);
	// this.save();
	//
	// }
	// }
	//
	// }
	// }
	//
	//
	// /**
	// * sets the organization lead's name to anonymous. and removes the
	// subscriber or
	// * the member in the organization from the lists
	// *
	// * added log statement inside the method to keep track of what happened
	// *
	// * @author Malak El Kommos
	// * @author Mai Selwanes
	// *
	// *
	// */
	//
	// public void modifyOrganization() {
	// // if (hasOrganization() == true) {
	// //
	// // User userName = this;
	// // List<Organization> organization = this.organization;
	// // for (int i = 0; i < organization.size(); i++) {
	// //
	// // if (!organization.get(i).subscribers.isEmpty())
	// // for (int m = 0; m < organization.get(i).subscribers.size(); m++) {
	// // Log log = new Log(
	// // organization.get(i),
	// // this,
	// // " named "
	// // + userName.name
	// // +
	// " has deleted his profile and so has been removed from the list of subscribers of the organization.");
	// // log.save();
	// // organization.get(i).subscribers.remove(m);
	// // this.save();
	// // }
	// //
	// // if (organization.get(i).lead != null) {
	// // Log log = new Log(
	// // organization.get(i),
	// // this,
	// // " named "
	// // + userName.name
	// // +
	// " (The Lead) has deleted his profile and so the lead has been named Anonymous.");
	// // log.save();
	// // organization.get(i).lead.name = "Anonymous";
	// // this.save();
	// // }
	// //
	// // if (!organization.get(i).orgMembers.isEmpty())
	// // for (int n = 0; n < organization.get(i).orgMembers.size(); n++) {
	// // Log log = new Log(
	// // organization.get(i),
	// // this,
	// // " named "
	// // + userName.name
	// // +
	// " has deleted his profile and so has been removed from the list of members of the orgnization.");
	// // log.save();
	// // organization.get(i).orgMembers.remove(n);
	// // this.save();
	// // }
	// // }
	// //
	// // }
	// // this.save();
	// }

	/**
	 * deletes a user profile
	 * 
	 * @author Malak El Kommos
	 * 
	 */

	public void deleteUser() {

		// this.modifyTopic();

		// this.modifyIdea();

		// this.modifyComment();

		// this.modifyEntity();

		// this.modifyOrganization();

		this.viewed = false;

		this.name = "Anonymous";
		this.email = null;
		this.contribution_preference = false;
		this.subscribe_preference = false;

		this.save();
	}

	/**
	 * takes a list of users that we need to send notifications to them,and call
	 * Notification constructor to create a new notification by calling
	 * Notification constructor. it will take input parameters:
	 * Content,ArrayList<User> Subscribers,type and typeId
	 * 
	 * @param content
	 *            the new Notification's body
	 * @param ArrayList
	 *            <User> subscribers the user(s) that the notification will be
	 *            send to.
	 * @param type
	 *            its the reason for initiating the notification, is it
	 *            topic,comment,idea.,written as ex: "Topic"
	 * @param typeId
	 *            its the id of the type
	 * @author youstina galal
	 */

	public void sendNotificationWithType(String content,
			List<User> subscribers, String type, Long typeId) {
		String writingContent = this.name + " " + content;

		for (int counter = 0; counter < subscribers.size(); counter++) {
			if (subscribers.get(counter).subscribe_preference == true) {

				Notification notify = new Notification(writingContent,
						subscribers.get(counter), type, typeId).save();
				subscribers.get(counter).notifies.add(notify);
				subscribers.get(counter).unreadNotification++;
			}

		}
	}

	/**
	 * constructor needed to get the user name when comment on idea.
	 * 
	 * @author Nada Hassan
	 */

	public User(String name) {
		this.name = name;
	}

	/**
	 * A method that returns the list of all users
	 * 
	 * @author Nourhan Mostafa Elsawa
	 */
	public static List<User> getUsers() {
		List<User> UserList = User.findAll();
		return (UserList);
	}

	/**
	 * creates a new relation request then adds it to the sender's
	 * requestRelations List and the reciever recieveRelations List
	 * 
	 * @author Menna Magdy
	 * 
	 * @param relationName
	 *            the new relation requested
	 * @param reciever
	 *            The user who will confirm/reject the request
	 *            "organizationLead"
	 */
	public void sendRelationRequest(Organization organization,
			String relationName) {
		String action = "A new Relation Name called " + relationName
				+ " is requested by " + this.name;
		RelationRequest request = new RelationRequest(this, organization,
				relationName, action).save();
		this.requestRelations.add(request);
		organization.receiveRelations.add(request);
		this.save();
		organization.save();
		ArrayList<User> users = new ArrayList<User>();
		users.add(organization.lead);
		this.sendNotificationWithType(action, users, "relationRequest",
				request.id);
	}

	/**
	 * notifyies the reciever with the relation requests.
	 * 
	 * @author Menna Magdy
	 * 
	 * @param reciever
	 *            The user who will confirm/reject the request
	 *            "organizationLead"
	 * @param message
	 *            the message that wants to be included in the notification
	 */
	public void replyRequest(User receiver, String message) {
		ArrayList<User> users = new ArrayList<User>();
		users.add(receiver);
		this.sendNotificationWithType(message, users, "relationRespond",
				receiver.id);
	}

	/**
	 * a method that returns all existing entities
	 * belonging to a certain user
	 * 
	 * @author Amr El Salanekly
	 */
	public List returnAllUsersEntities() {
		return this.userEntities;
	}

	public List<Organization> getOrganization() {
		return organization;
	}

	/**
	 * Access the database to the table of the permission and return the
	 * permission for a certain user in a certain organization
	 * 
	 * @param userId
	 *            A long that indicates the user id
	 * @param organizationId
	 *            A long that indicates the organization id
	 * @return role A permission that indicates the permission of the
	 *         user(userId) in the organization(organizationId)
	 * @author Nourhan El Sawa
	 */

	public static Permission userPermissionInOrganization(long userId,
			long organizationId) {
		User user = User.findById(userId);
		Organization organization = Organization.findById(organizationId);
		Permission role = Permission
				.find("user=? AND organization=? AND entity=null", user,
						organization).first();
		// System.out.println(role.aUsertype+ "in user model");
		return (role);
	}

	/**
	 * Access the database to the table of the permission and return the
	 * permission for a certain user in a certain entity in a certain
	 * organization.
	 * 
	 * @param userId
	 *            A long that indicates the user id.
	 * @param organizationId
	 *            A long that indicates the organization id.
	 * @param entityId
	 *            A long that indicates the entity id.
	 * @return role A permission that indicates the permission of the
	 *         user(userId) in an entity(entityId) in an
	 *         organization(organizationId)
	 * @author Nourhan El Sawa
	 */

	public static Permission userPermissionInEntity(long userId,
			long organizationId, long entityId) {

		Permission role = Permission.find(
				"USER_ID=? AND ORGANIZATION_ID=? AND ENTITY_ID=?", userId,
				organizationId, entityId).first();
		// System.out.println(role.aUsertype + "in user model");
		return (role);
	}

	/**
	 * creates a request for a topic and sends a
	 * notification and e-mail to the entity organizers and the organization
	 * lead.
	 * 
	 * @param title
	 *            Title of the request
	 * @param body
	 *            Body of the request
	 * @param entityId
	 *            The id of the entity that the request will be sent to its
	 *            organizers.
	 * 
	 * @author Shahir El Faramawy
	 */
	public void sendTopicRequest(String title, String body, Long entityId) {
		Entities entity = Entities.findById(entityId);
		Request request = new Request(title, body, entity, this);
		request.save();
		List<User> receivers = new ArrayList<User>();
		for (int counter = 0; counter < entity.entityOrganizers.size(); counter++) {

			receivers.add(entity.entityOrganizers.get(counter));

		}
		this.save();
		receivers.add(entity.organization.lead);
		this.sendNotificationWithType(" has requested a topic: ", receivers,
				"Request", request.id);
	}

	/**
	 * checks whether this email is already on the system or not
	 * 
	 * @param email
	 * @author Sara Assem
	 */
	public static boolean compareEmail(String email) {
		List<User> users = User.findAll();
		for (int i = 0; i < users.size(); i++) {
			if (users.get(i).email.equalsIgnoreCase(email))
				return true;

		}
		return false;
	}
	/**
	 * returns a boolean that specifies whether the comment was spammed by this user
	 * before or not
	 * @param commentId
	 *  	the comment id to retrieve the comment
	 * @return
	 * 	 	the boolean whether true or faulse
	 * @author Shahir El Faramawy
	 */
	public boolean commentSpamedBefore(Long commentId){
		Comment comment =Comment.findById(commentId);
		boolean isSpammed = false;
		for (int counter = 0; counter <= this.commentSpammed.size(); counter++) {
			if (this.id == comment.commentSpamReporters.get(counter).id) {
				isSpammed = true;
			}
		}
		return isSpammed;
	}
}
